#include "metanet_daemon.h"

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/file.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>

static int
check_pid(const char *pidfile) {
    int pid = 0;
    FILE *f = fopen(pidfile, "r");
    if (f == NULL) {
        return 0;
    }
    int n = fscanf(f, "%d", &pid);
    fclose(f);
    
    if (n != 1 || pid == 0 || pid == getpid()) {
        return 0;
    }

    if (kill(pid, 0) && errno == ESRCH) {
        return 0;
    }
    return pid;
}

static int
write_pid(const char *pidfile) {
    FILE *f;
    int pid = 0;
    int fd = open(pidfile, O_RDWR | O_CREAT, 0644);
    if (fd == -1) {
        fprintf(stderr, "Can't create pidfile [%s].\n", pidfile);
        return 0;
    }
    f = fdopen(fd, "w+");
    if (f == NULL) {
        fprintf(stderr, "Can't open pidfile [%s].\n", pidfile);
        return 0;
    }

    if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
        int n = fscanf(f, "%d", &pid);
        fclose(f);
        if (n != 1) {
            fprintf(stderr, "Can't lock and read pidfile.\n");
        } else {
            fprintf(stderr, "Can't lock pidfile, lock is held by pid %d.\n", pid);
        }
        return 0;
    }

    pid = getpid();
    if (!fprintf(f, "%d\n", pid)) {
        fprintf(stderr, "Can't write pid.\n'");
        close(fd);
        return 0;
    }
    fflush(f);
    return pid;
}

static int
redirect_fds() {
    int nfd = open("/dev/null", O_RDWR);
    if (nfd == -1) {
        perror("Unable to open /dev/null:");
        return -1;
    }
    if (dup2(nfd, 0) < 0) {
        perror("Unable to dup2 stdin(0)");
        return -1;
    }
    if (dup2(nfd, 1) < 0) {
        perror("Unable to dup2 stdout(1)");
        return -1;
    }
    if (dup2(nfd, 2) < 0) {
        perror("Unable to dup2 stdout(2)");
        return -1;
    }
    return 0;
}

int
daemon_init(const char *pidfile) {
    int pid = check_pid(pidfile);
    if (pid) {
        fprintf(stderr, "metanet is already running, pid=%d.\n", pid);
        return 1;
    }
    if (daemon(1, 1)) {
        fprintf(stderr, "Can't daemonize.\n");
        return 1;
    }
    pid = write_pid(pidfile);
    if (pid == 0) {
        return 1;
    }
    if (redirect_fds()) {
        return 1;
    }
    return 0;
}

int 
daemon_exit(const char *pidfile) {
    return unlink(pidfile);
}